"""生存模式。和常规模式相比，该模式将饥饿值显示的队列修改为400full，默认禁用作弊，每过2天改变生物群系，当全部群系的生活完成之后获胜。
level更改为4进1，level>=8时为large，此时速度将降低。在small/mid和large时，加入daily消耗(分别为1、2,3)
未来更新计划：加入家园，在生存模式中吃掉鱼有概率获得鱼苗，通关后可带回培养。(1.6)"""
import pygame
import random as r
import config
import psutil
import starlight
import strontium
import BiomeFilter
from typing import Union
import rubidium
import cesium
import achieve

if config.render_engine == 'starlights':
    RenderEngine = starlight
elif config.render_engine == 'strontium':
    RenderEngine = strontium
else:
    raise BufferError()

Optimize_Engine = rubidium
if config.optimize_engine == 'cesium':
    Optimize_Engine = cesium

bgpic : pygame.surface.Surface
play_isend = False
gpu_isend = False

def ftps():
    return pygame.time.get_ticks()
def prob(p : int) -> bool:
    return True if r.randint(0, 100) < p else False
def cint(d) -> int:
    return d if type(d) is int else int(d)

class ios(object):
    """处理文字输入和显示"""
    def __init__(self) -> None:
        self.text : str = ''
    
    def __lshift__(self, k):
        """input"""
        cmd = self.text
        kp = pygame.key.get_pressed()
        if k == pygame.K_a:
            cmd += "a"
        elif k == pygame.K_b:
            cmd += "B" if kp[pygame.K_LSHIFT] or kp[pygame.K_RSHIFT] else 'b'
        elif k == pygame.K_c:
            cmd += "c"
        elif k == pygame.K_d:
            cmd += "d"
        elif k == pygame.K_e:
            cmd += "e"
        elif k == pygame.K_f:
            cmd += "f"
        elif k == pygame.K_g:
            cmd += "g"
        elif k == pygame.K_h:
            cmd += "h"
        elif k == pygame.K_i:
            cmd += "i"
        elif k == pygame.K_j:
            cmd += "j"
        elif k == pygame.K_k:
            cmd += "k"
        elif k == pygame.K_l:
            cmd += "l"
        elif k == pygame.K_m:
            cmd += "m"
        elif k == pygame.K_n:
            cmd += "n"
        elif k == pygame.K_o:
            cmd += "o"
        elif k == pygame.K_p:
            cmd += "p"
        elif k == pygame.K_q:
            cmd += "q"
        elif k == pygame.K_r:
            cmd += "r"
        elif k == pygame.K_s:
            cmd += "s"
        elif k == pygame.K_t:
            cmd += "t"
        elif k == pygame.K_u:
            cmd += "u"
        elif k == pygame.K_v:
            cmd += "v"
        elif k == pygame.K_w:
            cmd += "w"
        elif k == pygame.K_x:
            cmd += "x"
        elif k == pygame.K_y:
            cmd += "y"
        elif k == pygame.K_z:
            cmd += "z"
        elif k == pygame.K_0:
            cmd += '0'
        elif k == pygame.K_1:
            cmd += "1"
        elif k == pygame.K_2:
            cmd += "2"
        elif k == pygame.K_3:
            cmd += "3"
        elif k == pygame.K_4:
            cmd += "4"
        elif k == pygame.K_5:
            cmd += "5"
        elif k == pygame.K_6:
            cmd += "6"
        elif k == pygame.K_7:
            cmd += "7"
        elif k == pygame.K_8:
            cmd += "8"
        elif k == pygame.K_9:
            cmd += "9"
        elif k == pygame.K_PERIOD:
            cmd += '.'
        elif k == pygame.K_SPACE:
            cmd += ' '
        elif k == pygame.K_MINUS:
            cmd += '_'
        elif k == pygame.K_BACKSPACE:
            if cmd != '':
                cmd = cmd[0:-1]
            if cmd == '':
                cmd = '/'
        self.text = cmd
    
    def clear(self):
        self.text = ''


hunger_reduce_calculator = Optimize_Engine.Queue(400, 0)
class player(object):
    def __init__(self):
        self.x = 800
        self.y = 450
        self.facing = 0 # 0-west 1-east
        self.speed_origin = r.randint(5, 7)
        self.speed = self.speed_origin
        self.Speed_i : bool = False
        self.level = 0
        self.hp = 1
        self.beatk = -1 # -1: 正常状态  0~30: 被攻击, 保护倒计时
        self.force = 1
        self.exp = 0 # small + 0~3, mid + 1~7, big + 3~15  exp = level*27 + 36 时升级 (36, 63, 90, 117, 144, 173...)
        # 被攻击一次，level降一级；按百分比降低。
        self.large_speed_reduce = r.randint(2, 4)
        self.imgl = config.player_s_l
        self.imgr = config.player_s_r
        self.hx = config.player_hx_s
        self.hy = config.player_hy_s
        self.footmax = 6435
        self.foot : int = self.footmax
        self.FOODmax = 15163
        self.FOOD : int = self.FOODmax
        self.hunger_add_sum_count = 0
    
    def run(self, frozen:bool, client_light:int):
        kp = pygame.key.get_pressed()
        speed_now = self.speed
        if self.level >= 8:
            speed_now -= self.large_speed_reduce
        feet_reduce : int = cint(speed_now / 2)
        if self.Speed_i == False or self.foot / self.footmax < 0.3:
            pass
        else:
            feet_reduce = speed_now
            speed_now = cint(speed_now * 2.3)
        if frozen == True and client_light < config.warm_light:
            speed_now = cint(speed_now / 2)
            feet_reduce = cint(speed_now * 2 / 3)
        #do_set_hunger_calcer = True
        if kp[pygame.K_LEFT] and self.x > speed_now:
            self.x -= speed_now
            self.facing = 0
            if self.Speed_i == True:
                feet_reduce += 1
            #hunger_reduce_calculator.push(feet_reduce)
            #do_set_hunger_calcer = False
            self.hunger(feet_reduce)
        if kp[pygame.K_RIGHT] and self.x + speed_now < 1650:
            self.x += speed_now
            self.facing = 1
            if self.Speed_i == True:
                feet_reduce += 1
            #hunger_reduce_calculator.push(feet_reduce)
            #do_set_hunger_calcer = False
            self.hunger(feet_reduce)
        if kp[pygame.K_UP] and self.y > speed_now:
            self.y -= speed_now
            if self.Speed_i == True:
                feet_reduce += 1
            #hunger_reduce_calculator.push(feet_reduce)
            #do_set_hunger_calcer = False
            self.hunger(feet_reduce)
        if kp[pygame.K_DOWN] and self.y + speed_now < 920:
            self.y += speed_now
            if self.Speed_i == True:
                feet_reduce += 1
            #hunger_reduce_calculator.push(feet_reduce)
            #do_set_hunger_calcer = False
            self.hunger(feet_reduce)
        #if do_set_hunger_calcer == True:
        #    hunger_reduce_calculator.push(0)
    
    def forceud(self):
        # force up and down
        if 6 <= self.level <= 14:
            self.force = 2
            self.imgl = config.player_m_l
            self.imgr = config.player_m_r
            self.hx = config.player_hx_m
            self.hy = config.player_hy_m
            
        elif self.level >= 15:
            self.force = 3
            self.imgl = config.player_l_l
            self.imgr = config.player_l_r
            self.hx = config.player_hx_l
            self.hy = config.player_hy_l
        else:
            self.force = 1
            self.imgl = config.player_s_l
            self.imgr = config.player_s_r
            self.hx = config.player_hx_s
            self.hy = config.player_hy_s

    def levelup(self):
        exp_max = 0
        if self.level <= 4:
            exp_max = self.level * 27 + 36
        elif self.level <= 12:
            exp_max = self.level ** 2 + 128
        else:
            exp_max = self.level ** 3 - 1456
        if self.exp >= exp_max:
            self.exp -= exp_max
            self.level += 1
            if self.level <= 5:
                self.speed += r.randint(1, 2) if self.speed <= 25 else 0
            self.forceud()
    
    def leveldown(self):
        if self.level <= 0:
            self.level = -1
        else:
            per = 0
            if self.level <= 4:
                per = self.exp / (self.level * 27 + 36)
            elif self.level <= 12:
                per = self.exp / (self.level ** 2 + 128)
            else:
                per = self.exp / (self.level ** 3 - 1456)
            self.level -= 1
            exp_max = 0
            if self.level <= 4:
                exp_max = self.level * 27 + 36
            elif self.level <= 12:
                exp_max = self.level ** 2 + 128
            else:
                exp_max = self.level ** 3 - 1456
            self.exp = cint(per * exp_max)
        if self.level <= 4:
            self.speed -= r.randint(1, 2) if self.speed >= 6 else 0
        self.forceud()
    
    def speed_half(self):
        #self.speed = cint(self.speed_origin * 2.3) if self.speed == self.speed_origin else self.speed_origin
        self.Speed_i = True if not self.Speed_i else False
    
    def daily(self, isnight : bool = False):
        if isnight == True:
            self.hunger(3)
        else:
            self.hunger(1)
    
    def hunger(self, count : int):
        c = count
        self.hunger_add_sum_count += c
        while self.FOOD > 0 and c > 0:
            self.FOOD -= 1
            c -= 1
        if c > 0:
            self.foot -= c
        if self.foot < 0:
            if config.gamemode == 'hard' and prob(1):
                self.foot = cint(self.footmax / 4)
                self.FOOD = cint(self.FOODmax * 3 / 4)
                self.leveldown()
            else:
                self.foot = 0

pl = player()

class sf(object):
    """small fishes"""
    def __init__(self) -> None:
        self.dtype = 'small'
        picfrom = r.choice(config.small_fishes)
        self.imgl = picfrom.imgl
        self.imgr = picfrom.imgr
        self.facing = 0
        self.hx = picfrom.hx
        self.hy = picfrom.hy
        self.hp = 1
        self.x, self.y = 0, 0
        if prob(50):
            self.x = r.randint(1800, 1920)
        else:
            self.x = r.randint(-380, -220)
        smart = r.randint(0, 2)
        if smart == 2:
            self.y = r.randint(1, 919)
        else:
            self.y = r.randint(200, 800)
        self.smart = smart
        self.speed_formerly = r.randint(config.mob['small']['speed'][0], config.mob['small']['speed'][1])
        self.speed = self.speed_formerly
        self.force = 1
        self.yway = 1 # y++
        self.yspeed = cint(self.speed * 2 / 3)
        if self.smart == 0:
            # 只能一直朝同一方向运动
            if self.x > 1600:
                self.facing = 0
            else:
                self.facing = 1
        elif self.smart == 1:
            # 左右方向随机移动
            pass
        elif self.smart == 2:
            # 四个方向随机移动
            pass
        elif self.smart == 3:
            # 追击玩家
            self.speed = r.randint(2, 6)
        else:
            pass
    
    def run(self, frozen:bool, client_light:int):
        if frozen == True and client_light <config.warm_light:
            self.speed = cint(self.speed_formerly / 2)
            self.yspeed= cint(self.speed * 2 / 3)
        else:
            self.speed = self.speed_formerly
            self.yspeed= cint(self.speed * 2 / 3)
        if self.smart == 0:
            if self.facing == 0:
                self.x -= self.speed
            else:
                self.x += self.speed
        elif self.smart == 1:
            if prob(80):
                if self.facing == 0:
                    self.x -= self.speed
                else:
                    self.x += self.speed
            else:
                if prob(50):
                    self.facing = 0
                    self.x -= self.speed
                else:
                    self.facing = 1
                    self.x += self.speed
        elif self.smart == 2:
            if prob(80):
                if self.facing == 0:
                    self.x -= self.speed
                else:
                    self.x += self.speed
            else:
                if prob(50):
                    self.facing = 0
                    self.x -= self.speed
                else:
                    self.facing = 1
                    self.x += self.speed
            if prob(70):
                if self.yway == 0:
                    self.y -= self.yspeed
                else:
                    self.y += self.yspeed
            else:
                if prob(50):
                    self.yway = 0
                    self.y -= self.yspeed
                else:
                    self.yway = 1
                    self.y += self.yspeed
        elif self.smart == 3:

            if self.x - pl.x >= self.speed:
                self.x -= self.speed
            elif pl.x - self.x >= self.speed:
                self.x += self.speed
            if self.y - pl.y >= self.yspeed:
                self.y -= self.yspeed
            elif pl.y - self.y >= self.yspeed:
                self.y += self.yspeed
    
class mf(object):
    """mid fishes"""
    def __init__(self) -> None:
        self.dtype = 'mid'
        picfrom = r.choice(config.middle_fishes)
        self.imgl = picfrom.imgl
        self.imgr = picfrom.imgr
        self.facing = 0
        self.hx = picfrom.hx
        self.hy = picfrom.hy
        self.hp = 1
        self.x, self.y = 0, 0
        if prob(50):
            self.x = r.randint(1800, 1920)
        else:
            self.x = r.randint(-380, -220)
        smart = r.randint(0, 2)
        if smart == 2:
            self.y = r.randint(1, 919)
        else:
            self.y = r.randint(200, 800)
        self.smart = smart
        self.speed_formerly = r.randint(config.mob['mid']['speed'][0], config.mob['mid']['speed'][1])
        self.speed = self.speed_formerly
        self.force = 2
        self.yway = 1 # y++
        self.yspeed = cint(self.speed * 2 / 3)
        self.goplan : list = [0 if self.x > 1600 else 1, # direct west(0) or east(1), or wait(2)
                       120, # run time. tps = 20, 120t = 6 sec.
        ]
        self.facing = self.goplan[0]
        if self.smart == 0:
            # 只能一直朝同一方向运动
            if self.x > 1600:
                self.facing = 0
            else:
                self.facing = 1
        elif self.smart == 1:
            # 左右方向随机移动
            pass
        elif self.smart == 2:
            # 四个方向随机移动
            pass
        elif self.smart == 3:
            # 追击玩家
            self.speed = r.randint(2, 6)
        else:
            pass
    
    def run(self, frozen:bool, client_light:int):
        if frozen == True and client_light < config.warm_light:
            self.speed = cint(self.speed_formerly / 2)
            self.yspeed= cint(self.speed * 2 / 3)
        else:
            self.speed = self.speed_formerly
            self.yspeed= cint(self.speed * 2 / 3)
        if self.smart == 0:
            if self.facing == 0:
                self.x -= self.speed
            else:
                self.x += self.speed
        elif self.smart == 1:
            if self.goplan[1] > 0:
                if self.goplan[0] == 0:
                    self.x -= self.speed
                elif self.goplan[0] == 1:
                    self.x += self.speed
                else:
                    if prob(12):
                        self.facing = 0 if self.facing == 1 else 1
                self.goplan[1] -= 1
            else:
                self.goplan[0] = r.randint(0, 2)
                if self.goplan[0] != 2:
                    self.goplan[1] = r.randint(16, 220)
                    self.facing = self.goplan[0]
                else:
                    self.goplan[1] = r.randint(16, 40)
        elif self.smart == 2:
            if self.goplan[1] > 0:
                if self.goplan[0] == 0:
                    self.x -= self.speed
                elif self.goplan[0] == 1:
                    self.x += self.speed
                else:
                    if prob(12):
                        self.facing = 0 if self.facing == 1 else 1
                self.goplan[1] -= 1
            else:
                self.goplan[0] = r.randint(0, 2)
                if self.goplan[0] != 2:
                    self.goplan[1] = r.randint(16, 220)
                    self.facing = self.goplan[0]
                else:
                    self.goplan[1] = r.randint(16, 40)
            if prob(90):
                if self.yway == 0:
                    self.y -= self.yspeed
                elif self.yway == 1:
                    self.y += self.yspeed
                else:
                    pass
            else:
                if prob(70):
                    self.yway = 2
                elif prob(50):
                    self.yway = 0
                    self.y -= self.yspeed
                else:
                    self.yway = 1
                    self.y += self.yspeed
        elif self.smart == 3:

            if self.x - pl.x >= self.speed:
                self.x -= self.speed
            elif pl.x - self.x >= self.speed:
                self.x += self.speed
            if self.y - pl.y >= self.yspeed:
                self.y -= self.yspeed
            elif pl.y - self.y >= self.yspeed:
                self.y += self.yspeed
class lf(object):
    """large fishes"""
    def __init__(self) -> None:
        self.dtype = 'large'
        picfrom = r.choice(config.large_fishes)
        self.imgl = picfrom.imgl
        self.imgr = picfrom.imgr
        self.facing = 0
        self.hx = picfrom.hx
        self.hy = picfrom.hy
        self.hp = 1
        self.x, self.y = 0, 0
        self.occur_time = ftps()
        if prob(50):
            self.x = r.randint(1800, 1920)
        else:
            self.x = r.randint(-380, -220)
        smart = 3 if prob(8) else r.randint(0, 2)
        if pl.force >= 3 and smart == 3:
            smart = 2
        if smart == 2:
            self.y = r.randint(1, 919)
        else:
            self.y = r.randint(200, 800)
        self.smart = smart
        self.speed_formerly = r.randint(config.mob['large']['speed'][0], config.mob['large']['speed'][1])
        self.speed = self.speed_formerly
        if self.smart == 3:
            self.speed_formerly = r.randint(config.mob['large']['smart_3_speed'][0], config.mob['large']['smart_3_speed'][1])
            self.speed = self.speed_formerly
        self.force = 3
        self.yway = 1 # y++
        self.yspeed = cint(self.speed * 2 / 3)
        self.goplan : list = [0 if self.x > 1600 else 1, # direct west(0) or east(1), or wait(2)
                       120, # run time. tps = 20, 120t = 6 sec.
        ]
        self.facing = self.goplan[0]
        if self.smart == 0:
            # 只能一直朝同一方向运动
            if self.x > 1600:
                self.facing = 0
            else:
                self.facing = 1
        elif self.smart == 1:
            # 左右方向随机移动
            pass
        elif self.smart == 2:
            # 四个方向随机移动
            pass
        elif self.smart == 3:
            # 追击玩家
            self.speed = r.randint(2, 6)
        else:
            pass
    
    def run(self, frozen:bool, client_light:int):
        if frozen == True and client_light < config.warm_light:
            self.speed = cint(self.speed_formerly / 2)
            self.yspeed= cint(self.speed * 2 / 3)
        else:
            self.speed = self.speed_formerly
            self.yspeed= cint(self.speed * 2 / 3)
        if self.smart == 0:
            if self.facing == 0:
                self.x -= self.speed
            else:
                self.x += self.speed
        elif self.smart == 1:
            if self.goplan[1] > 0:
                if self.goplan[0] == 0:
                    self.x -= self.speed
                elif self.goplan[0] == 1:
                    self.x += self.speed
                else:
                    if prob(12):
                        self.facing = 0 if self.facing == 1 else 1
                self.goplan[1] -= 1
            else:
                self.goplan[0] = r.randint(0, 2)
                if self.goplan[0] != 2:
                    self.goplan[1] = r.randint(16, 220)
                    self.facing = self.goplan[0]
                else:
                    self.goplan[1] = r.randint(16, 40)
        elif self.smart == 2:
            if self.goplan[1] > 0:
                if self.goplan[0] == 0:
                    self.x -= self.speed
                elif self.goplan[0] == 1:
                    self.x += self.speed
                else:
                    if prob(12):
                        self.facing = 0 if self.facing == 1 else 1
                self.goplan[1] -= 1
            else:
                self.goplan[0] = r.randint(0, 2)
                if self.goplan[0] != 2:
                    self.goplan[1] = r.randint(16, 220)
                    self.facing = self.goplan[0]
                else:
                    self.goplan[1] = r.randint(16, 40)
            if prob(82):
                if self.yway == 0:
                    self.y -= self.yspeed
                else:
                    self.y += self.yspeed
            else:
                if prob(50):
                    self.yway = 0
                    self.y -= self.yspeed
                else:
                    self.yway = 1
                    self.y += self.yspeed
        elif self.smart == 3:
            self.goplan[1] = 1
            if not ftps() >= self.occur_time + 30_000 and pl.force < self.force:
                if (760 < pl.x < 890) and (760 < pl.y < 920):
                    # in safe place(area)
                    if self.goplan[1] > 0:
                        if self.goplan[0] == 0:
                            self.x -= self.speed
                            self.facing = 0
                        elif self.goplan[0] == 1:
                            self.x += self.speed
                            self.facing = 1
                        else:
                            if prob(12):
                                self.facing = 0 if self.facing == 1 else 1
                        self.goplan[1] -= 1
                    else:
                        self.goplan[0] = r.randint(0, 2)
                        if self.goplan[0] != 2:
                            self.goplan[1] = r.randint(16, 220)
                            self.facing = self.goplan[0]
                        else:
                            self.goplan[1] = r.randint(16, 40)
                else:
                    if self.x - pl.x >= self.speed:
                        self.x -= self.speed
                        self.facing = 0
                    elif pl.x - self.x >= self.speed:
                        self.x += self.speed
                        self.facing = 1
                    if self.y - pl.y >= self.yspeed:
                        self.y -= self.yspeed
                    elif pl.y - self.y >= self.yspeed:
                        self.y += self.yspeed
            else:
                self.smart = 2

class Lantern_Fish(object):
    """lantern fishes"""
    def __init__(self) -> None:
        self.dtype = 'lanternfish'
        picfrom = r.choice(config.lantern_fishes)
        multipler: int = r.randint(1, 7)
        self.imgl = pygame.transform.scale(picfrom.imgl, (picfrom.hx * 2 * multipler, picfrom.hy * 2 * multipler))
        self.imgr = pygame.transform.scale(picfrom.imgr, (picfrom.hx * 2 * multipler, picfrom.hy * 2 * multipler))
        self.facing = 0
        self.hx = picfrom.hx * multipler
        self.hy = picfrom.hy * multipler
        self.hp = 1
        self.x, self.y = 0, 0
        self.light_block_value = r.randint(config.mob['lantern']['light_block_value'][0], config.mob['lantern']['light_block_value'][1])
        if prob(50):
            self.x = r.randint(1800, 1920)
        else:
            self.x = r.randint(-380, -220)
        smart = r.randint(0, 2)
        if smart == 2:
            self.y = r.randint(1, 919)
        else:
            self.y = r.randint(200, 800)
        self.smart = smart
        self.speed = r.randint(config.mob['lantern']['speed'][0], config.mob['lantern']['speed'][1])
        self.force = 1 if self.hx <= 32 else 2 if self.hx <= 53 else 3 if self.hx <= 70 else 4
        self.foot = r.randint(20, 169) if self.force != 1 else r.randint(2028, 4096)
        self.feet = r.randint(705, 1096) if self.force != 1 else r.randint(20, 169)
        self.yway = 1 # y++
        self.yspeed = cint(self.speed * 2 / 3)
        self.goplan : list = [0 if self.x > 1600 else 1, # direct west(0) or east(1), or wait(2)
                       120, # run time. tps = 20, 120t = 6 sec.
        ]
        self.facing = self.goplan[0]
        if self.smart == 0:
            # 只能一直朝同一方向运动
            if self.x > 1600:
                self.facing = 0
            else:
                self.facing = 1
        elif self.smart == 1:
            # 左右方向随机移动
            pass
        elif self.smart == 2:
            # 四个方向随机移动
            pass
        elif self.smart == 3:
            # 追击玩家
            self.speed = r.randint(2, 6)
        else:
            pass
    
    def run(self, frozen:bool, light_client:bool):
        if self.smart == 0:
            if self.facing == 0:
                self.x -= self.speed
            else:
                self.x += self.speed
        elif self.smart == 1:
            if self.goplan[1] > 0:
                if self.goplan[0] == 0:
                    self.x -= self.speed
                elif self.goplan[0] == 1:
                    self.x += self.speed
                else:
                    if prob(12):
                        self.facing = 0 if self.facing == 1 else 1
                self.goplan[1] -= 1
            else:
                self.goplan[0] = r.randint(0, 2)
                if self.goplan[0] != 2:
                    self.goplan[1] = r.randint(16, 220)
                    self.facing = self.goplan[0]
                else:
                    self.goplan[1] = r.randint(16, 40)
        elif self.smart == 2:
            if self.goplan[1] > 0:
                if self.goplan[0] == 0:
                    self.x -= self.speed
                elif self.goplan[0] == 1:
                    self.x += self.speed
                else:
                    if prob(12):
                        self.facing = 0 if self.facing == 1 else 1
                self.goplan[1] -= 1
            else:
                self.goplan[0] = r.randint(0, 2)
                if self.goplan[0] != 2:
                    self.goplan[1] = r.randint(16, 220)
                    self.facing = self.goplan[0]
                else:
                    self.goplan[1] = r.randint(16, 40)
            if prob(90):
                if self.yway == 0:
                    self.y -= self.yspeed
                elif self.yway == 1:
                    self.y += self.yspeed
                else:
                    pass
            else:
                if prob(70):
                    self.yway = 2
                elif prob(50):
                    self.yway = 0
                    self.y -= self.yspeed
                else:
                    self.yway = 1
                    self.y += self.yspeed
        elif self.smart == 3:

            if self.x - pl.x >= self.speed:
                self.x -= self.speed
            elif pl.x - self.x >= self.speed:
                self.x += self.speed
            if self.y - pl.y >= self.yspeed:
                self.y -= self.yspeed
            elif pl.y - self.y >= self.yspeed:
                self.y += self.yspeed

class shark(object):
    """Shark"""
    def __init__(self) -> None:
        self.dtype = 'shark'
        picfrom = r.choice(config.shark_fishes)
        self.imgl = picfrom.imgl
        self.imgr = picfrom.imgr
        self.facing = 0
        self.hx = picfrom.hx
        self.hy = picfrom.hy
        self.hp = 1
        self.x, self.y = 0, 0
        if prob(50):
            self.x = r.randint(1800, 1920)
        else:
            self.x = r.randint(-380, -220)
        smart = 0
        if smart == 0:
            self.y = r.randint(1, 919)
        else:
            self.y = r.randint(-200, 1080)
        self.smart = smart
        self.speed_formerly = r.randint(config.mob['shark']['speed'][0], config.mob['shark']['speed'][1])
        self.speed = self.speed_formerly
        self.force = 4
        self.yway = 1 # y++
        self.yspeed = cint(self.speed * 2 / 3)
        self.goplan : list = [0 if self.x > 1600 else 1, # direct west(0) or east(1), or wait(2)
                       120, # run time. tps = 20, 120t = 6 sec.
        ]
        self.facing = self.goplan[0]
        if self.smart == 0:
            # 只能一直朝同一方向运动
            if self.x > 1600:
                self.facing = 0
            else:
                self.facing = 1
        elif self.smart == 1:
            # 左右方向随机移动
            pass
        elif self.smart == 2:
            # 四个方向随机移动
            pass
        elif self.smart == 3:
            # 追击玩家
            self.speed = r.randint(2, 6)
        else:
            pass
    
    def run(self, frozen:bool, client_light:int):
        if frozen == True and client_light < config.warm_light:
            self.speed = cint(self.speed_formerly / 2)
            self.yspeed= cint(self.speed * 2 / 3)
        else:
            self.speed = self.speed_formerly
            self.yspeed= cint(self.speed * 2 / 3)
        if self.smart == 0:
            if self.facing == 0:
                self.x -= self.speed
            else:
                self.x += self.speed
        elif self.smart == 1:
            if self.goplan[1] > 0:
                if self.goplan[0] == 0:
                    self.x -= self.speed
                elif self.goplan[0] == 1:
                    self.x += self.speed
                else:
                    if prob(12):
                        self.facing = 0 if self.facing == 1 else 1
                self.goplan[1] -= 1
            else:
                self.goplan[0] = r.randint(0, 2)
                if self.goplan[0] != 2:
                    self.goplan[1] = r.randint(16, 220)
                    self.facing = self.goplan[0]
                else:
                    self.goplan[1] = r.randint(16, 40)
        elif self.smart == 2:
            if self.goplan[1] > 0:
                if self.goplan[0] == 0:
                    self.x -= self.speed
                elif self.goplan[0] == 1:
                    self.x += self.speed
                else:
                    if prob(12):
                        self.facing = 0 if self.facing == 1 else 1
                self.goplan[1] -= 1
            else:
                self.goplan[0] = r.randint(0, 2)
                if self.goplan[0] != 2:
                    self.goplan[1] = r.randint(16, 220)
                    self.facing = self.goplan[0]
                else:
                    self.goplan[1] = r.randint(16, 40)
            if prob(90):
                if self.yway == 0:
                    self.y -= self.yspeed
                elif self.yway == 1:
                    self.y += self.yspeed
                else:
                    pass
            else:
                if prob(70):
                    self.yway = 2
                elif prob(50):
                    self.yway = 0
                    self.y -= self.yspeed
                else:
                    self.yway = 1
                    self.y += self.yspeed

class su(object):
    """sea urchin"""
    def __init__(self) -> None:
        self.dtype = 'su'
        picfrom = r.choice(config.sea_urchin)
        self.facing = 0
        xset = r.randint(40, 100)
        self.hx = cint(xset / 2)
        self.hy = self.hx
        self.imgl = pygame.transform.scale(picfrom, (xset, xset))
        self.imgr = pygame.transform.scale(picfrom, (xset, xset))
        self.hp = 1
        self.x, self.y = 0, 0
        if prob(50):
            self.x = r.randint(1800, 1920)
        else:
            self.x = r.randint(-380, -220)
        self.y = r.randint(1, 919)
        self.speed_formerly = r.randint(config.mob['su']['speed'][0], config.mob['su']['speed'][1])
        self.speed = self.speed_formerly
        self.force = 5
        self.yway = 1 # y++
        self.yspeed = cint(self.speed * 2 / 3)
        self.goplan : list = [0 if self.x > 1600 else 1, # direct west(0) or east(1), or wait(2)
                       120, # run time. tps = 20, 120t = 6 sec.
        ]
        self.facing = self.goplan[0]
        if self.x > 1600:
            self.facing = 0
        else:
            self.facing = 1
        self.επιπλέει = True
        if prob(68):
            self.επιπλέει = False
        if self.επιπλέει == True:
            self.speed_formerly = r.randint(config.mob['su']['float_speed'][0], config.mob['su']['float_speed'][1])
            self.speed = self.speed_formerly
        self.επιπλέει_speed = r.randint(config.mob['su']['water_speed'][0], config.mob['su']['water_speed'][1])
        self.y_gotime = 0
    
    def run(self, frozen:bool, client_light:bool):
        if frozen == True and client_light <config.warm_light:
            self.speed = max(cint(self.speed_formerly / 2), 1)
            self.yspeed= max(cint(self.speed * 2 / 3), 1)
        else:
            self.speed = self.speed_formerly
            self.yspeed= cint(self.speed * 2 / 3)
        if self.επιπλέει == False:
            if self.goplan[1] > 0:
                if self.goplan[0] == 0:
                    self.x -= self.speed
                elif self.goplan[0] == 1:
                    self.x += self.speed
                else:
                    pass
                self.goplan[1] -= 1
            else:
                self.goplan[0] = r.randint(0, 2)
                if self.goplan[0] != 2:
                    self.goplan[1] = r.randint(16, 220)
                    self.facing = self.goplan[0]
                else:
                    self.goplan[1] = r.randint(16, 40)
        else:
            if self.goplan[1] > 0:
                if self.goplan[0] == 0:
                    self.x -= self.επιπλέει_speed
                elif self.goplan[0] == 1:
                    self.x += self.επιπλέει_speed
                else:
                    if prob(3):
                        self.x -= self.επιπλέει_speed
                    elif prob(3):
                        self.x += self.επιπλέει_speed
                self.goplan[1] -= 1
            else:
                self.goplan[0] = r.randint(0, 2)
                if self.goplan[0] != 2:
                    self.goplan[1] = r.randint(64, 512)
                    self.facing = self.goplan[0]
                else:
                    self.goplan[1] = r.randint(256, 512)
        # 受重力作用下沉
        if prob(40):
            self.y += 1
        if self.επιπλέει == False:
            if prob(90) and not (620 <= self.y <= 890):
                # 有 90% 概率向沙滩前进
                if self.y <= 620:
                    self.y += self.yspeed
                elif self.y >= 890:
                    self.y -= self.yspeed
            elif prob(90):
                if self.yway == 0:
                    self.y -= self.yspeed
                elif self.yway == 1:
                    self.y += self.yspeed
                else:
                    pass
            else:
                if prob(70):
                    self.yway = 2
                elif prob(50):
                    self.yway = 0
                    self.y -= self.yspeed
                else:
                    self.yway = 1
                    self.y += self.yspeed
        else:
            if self.y_gotime > 0:
                if prob(30) == True:
                    if self.yway == 0:
                        self.y -= 1
                    elif self.yway == 1:
                        self.y += 1
                    else:
                        if prob(3):
                            self.y -= 1
                        elif prob(3):
                            self.y += 1
                self.y_gotime -= 1
            else:
                self.yway = r.randint(0, 2)
                if self.yway != 2:
                    self.y_gotime = r.randint(64, 512)
                else:
                    self.y_gotime = r.randint(256, 512)

class iceSpikes(object):
    """冰刺"""
    def __init__(self) -> None:
        self.x = r.randint(0, 1650)
        self.y = -200
        self.hx = r.randint(40, 120)
        self.hy = r.randint(60, 150)
        self.yspeed = r.randint(config.ice_spike_yspeed[0], config.ice_spike_yspeed[1])
        self.pic = pygame.Surface((self.hx * 2, self.hy * 2))
        self.pic.fill((10, 20, r.randint(200, 255)))

def calc_local_difficulty_max(biome: str) -> float:
    small, m, l, lant, sh, su = (config.biome[biome]['mob_list']['small']['day']+config.biome[biome]['mob_list']['small']['night'])/2,\
        (config.biome[biome]['mob_list']['mid']['day']+config.biome[biome]['mob_list']['mid']['night'])/2,\
        (config.biome[biome]['mob_list']['large']['day']+config.biome[biome]['mob_list']['large']['night'])/2,\
        (config.biome[biome]['mob_list']['lantern']['day']+config.biome[biome]['mob_list']['lantern']['night'])/2,\
        (config.biome[biome]['mob_list']['shark']['day']+config.biome[biome]['mob_list']['shark']['night'])/2,\
        (config.biome[biome]['mob_list']['sea_urchin']['day']+config.biome[biome]['mob_list']['sea_urchin']['night'])/2
    small /= 100
    m /= 100
    l /= 100
    lant /= 100
    sh /= 100
    su /= 100
    lantern = lant
    mid = (1-lant) * (1-small) * m
    large=(1-mid)*l
    shark=(1-large) * sh
    su = (1-shark) * su
    i = 20*mid*2.33 + 20*large*6.27 + 20*lantern*13.74 + 20*shark*17.91 + 20*su*19.54 + 67.51
    return i

fish_list = []
biome_now : str = 'ocean'

class dialog(object):
    """对话框。保存要显示的文本和剩余显示时间"""
    def __init__(self, text : str, times : int = ftps() + 3_200, red : bool = False, yellow: bool = False, purple:bool = False) -> None:
        self.text = text
        self.times = times + 1_000
        self.red = red
        self.yellow = yellow
        self.purple = purple

def play():
    global play_isend, fish_list, biome_now
    bgpic = r.choice(config.bgs)
    f2f = pygame.font.Font('C:/Windows/Fonts/simhei.ttf', 0x0f)
    biome_now_canuse : list = config.biome['allow']
    r.shuffle(biome_now_canuse)
    biome_now = biome_now_canuse[0]
    biome_now_index = 0
    is_frozen: bool = config.biome[biome_now]['frozen']
    BF : Union[None, BiomeFilter.Ocean, BiomeFilter.Deep_Ocean, BiomeFilter.Warm_Ocean, BiomeFilter.Cold_Ocean]\
        = BiomeFilter.set_biome(biome_now, bgpic)
    ice_spike_addtime = ftps() + 8_000 # 每隔一段时间, 尝试添加一组冰刺, 每组有1-3个.
    ice_spike_list: list = []
    ach, achTitle = achieve.ask_custom(achieve.achievement.custom.PopularScenicSpots, biome_now)
    dusk_render: BiomeFilter.Dusk = BiomeFilter.Dusk()
    tps = ftps()
    fish_list_new = []
    hunger_bar : pygame.surface.Surface = pygame.Surface((500, 24))
    xp_bar : pygame.surface.Surface = pygame.Surface((500, 24))
    hunger_bar.set_alpha(128)
    xp_bar.set_alpha(128)
    hunger_color = (r.randint(0, 255), r.randint(0, 255), r.randint(0, 255))
    xp_color = (r.randint(0, 255), r.randint(0, 255), r.randint(0, 255))
    hunger_color_anti = [0, 0, 0]
    xp_color_anti = [0, 0, 0]
    for i in range(3):
        hunger_color_anti[i] = RenderEngine.mid(255-hunger_color[i], 0, 255)
        xp_color_anti[i] = RenderEngine.mid(255 - xp_color[i], 0, 255)
    hunger_color_anti = tuple(hunger_color_anti)
    xp_color_anti = tuple(xp_color_anti)
    fps_time = ftps()
    fps_get_now = 0
    cpu_time = ftps()
    cpu_get_now = 0
    mem_get_now = 0
    cpufreq_get_now = 0
    start_render_time = ftps()
    day_night_cycle_pause_time = ftps()
    daily_tps = ftps()
    do_show_debug = False
    do_show_box = False
    do_pausing = False
    do_input_command = False
    do_show_text_past = True
    cmd : ios = ios()
    show_text = [] # 命令执行结果提示 (dialogs)
    show_text_past = []
    sunrise_time = ftps() - 6101 # 日出时间。0~6_000 sunrise, 6_000~600_000 day, 600_000~606_000 sunset, 606_000~1200_000 night
    light_dark : int = -1  # 普通光照黑暗度, [-1, 120]
    light_dark_add_tps = ftps()
    phosphorus = RenderEngine.Sun(config.screen)
    Phosper = RenderEngine.Ray_Tracing(config.screen, False, config.γ)
    rule_using_RT : bool = config.using_ray_tracing
    Lantern_Fish_list : list = []
    day : int = 0
    local_difficulty : float = 0 # max: 140 + 
    local_difficulty_mob_add : float = 0.0
    local_difficulty_max : float = calc_local_difficulty_max(biome_now) # local_difficulty + 17.51 + 25 * 17.91
    local_difficulty_tps : int = ftps()
    local_difficulty_per : int = 0
    local_difficulty_show = local_difficulty

    if ach == True:
        pl.exp += r.randint(10, 50)
        show_text.append(dialog(f'完成挑战: {achTitle}', purple=True))
    elif ach == False:
        show_text.append(dialog(f'完成进度: {achTitle}', yellow=True))

    """
    def reset_biome(what:str = 'random'):
        nonlocal local_difficulty_max, BF, is_frozen
        global biome_now
        biome_now_canuse_inreset:list = config.biome['allow']
        if what == 'random':
            biome_now = r.choice(biome_now_canuse_inreset)
        else:
            if what in biome_now_canuse_inreset:
                biome_now = what
            else:
                return False
        is_frozen = config.biome[biome_now]['frozen']
        BF = BiomeFilter.set_biome(biome_now, bgpic)
        local_difficulty_max = calc_local_difficulty_max(biome_now)
        return True
    """

    while not config.main_isend:
        local_difficulty = 0
        if play_isend:
            return 0
        if ftps() >= daily_tps and not do_pausing:
            daily_tps = ftps() + 320
            pl.daily(False if ftps()-sunrise_time < 600_000 else True)
            if pl.level < 6:
                pass # pl.hunger(1)
            elif pl.level < 15:
                pl.hunger(3)
            else:
                pl.hunger(5)
            if is_frozen == True and Phosper.get_client(pl.x, pl.y) < config.warm_light:
                pl.hunger(1)
            if pl.Speed_i == True:
                pl.hunger(1)
        if do_pausing == True:
            sunrise_time = ftps() - day_night_cycle_pause_time
        if ftps() >= tps and do_pausing == False:
            tps = ftps() + 50
            pl.run(is_frozen, Phosper.get_client(pl.x, pl.y))
            if pl.beatk >= 0:
                pl.beatk -= 1
            fish_list_new = []
            Lantern_Fish_list = []
            local_difficulty_mob_add = 0.0
            for f in fish_list:
                if f.hp > 0:
                    f.run(is_frozen, Phosper.get_client(f.x, f.y))
                    if -300 < f.x < 1930 and 0 < f.y < 920:
                        fish_list_new.append(f)
                        if f.dtype == 'lanternfish':
                            Lantern_Fish_list.append(RenderEngine.light_block(f.x, f.y, f.light_block_value))
                            local_difficulty_mob_add += 13.74
                        elif f.dtype == 'mid':
                            local_difficulty_mob_add += 2.33
                        elif f.dtype == 'large':
                            local_difficulty_mob_add += 6.27
                        elif f.dtype == 'shark':
                            local_difficulty_mob_add += 17.91
                        elif f.dtype == 'su':
                            local_difficulty_mob_add += 19.54
            if 24_000 < ftps() - sunrise_time < 606_000:
                # day
                if len(fish_list_new) < 15 and prob(6):
                    if prob(config.biome[biome_now]['mob_list']['lantern']['day']):
                        fish_list_new.append(Lantern_Fish())
                    if prob(config.biome[biome_now]['mob_list']['small']['day']):
                        fish_list_new.append(sf())
                    elif prob(config.biome[biome_now]['mob_list']['mid']['day']):
                        fish_list_new.append(mf())
                    elif prob(config.biome[biome_now]['mob_list']['large']['day']):
                        fish_list_new.append(lf())
                    elif prob(config.biome[biome_now]['mob_list']['shark']['day']):
                        fish_list_new.append(shark())
                    elif prob(config.biome[biome_now]['mob_list']['sea_urchin']['day']):
                        fish_list_new.append(su())
                    else:
                        pass
            else:
                # night
                local_difficulty_mob_add += 67.51
                if len(fish_list_new) < 25 and prob(8):
                    if prob(config.biome[biome_now]['mob_list']['lantern']['night']):
                        fish_list_new.append(Lantern_Fish())
                    elif prob(config.biome[biome_now]['mob_list']['small']['night']):
                        fish_list_new.append(sf())
                    elif prob(config.biome[biome_now]['mob_list']['mid']['night']):
                        fish_list_new.append(mf())
                    elif prob(config.biome[biome_now]['mob_list']['large']['night']):
                        fish_list_new.append(lf())
                    elif prob(config.biome[biome_now]['mob_list']['shark']['night']):
                        fish_list_new.append(shark())
                    elif prob(config.biome[biome_now]['mob_list']['sea_urchin']['night']):
                        fish_list_new.append(su())
                    else:
                        pass
            fish_list = fish_list_new
            for f in fish_list:
                if abs(pl.x - f.x) <= f.hx + pl.hx and abs(pl.y - f.y) <= f.hy + pl.hy:
                    if pl.force >= f.force:
                        f.hp = 0
                        if f.dtype == 'small':
                            pl.exp += r.randint(0, 3)
                            pl.foot += r.randint(60, 120)
                            pl.FOOD += r.randint(20, 380)
                        elif f.dtype == 'mid':
                            pl.exp += r.randint(1, 4)
                            pl.foot += r.randint(120, 180)
                            pl.FOOD += r.randint(180, 320)
                        elif f.dtype == 'large':
                            pl.exp += r.randint(2, 5)
                            pl.foot += r.randint(220, 269)
                            pl.FOOD += r.randint(60, 168)
                        elif f.dtype == 'lanternfish':
                            pl.exp += r.randint(1, 30)
                            pl.foot += f.foot
                            pl.FOOD += f.feet
                            ach, achTitle = achieve.ask(achieve.achievement.adventure.isThatLantern)
                            if ach == True:
                                pl.exp += r.randint(10, 50)
                                pl.levelup()
                                show_text.append(dialog(f'完成挑战: {achTitle}', purple=True))
                            elif ach == False:
                                show_text.append(dialog(f'完成进度: {achTitle}', yellow=True))
                        if pl.foot > pl.footmax:
                            pl.foot = pl.footmax
                        if pl.FOOD > pl.FOODmax:
                            pl.FOOD = pl.FOODmax
                        pl.levelup()
                        if pl.level == 6:
                            ach, achTitle = achieve.ask(achieve.achievement.adventure.levelup)
                            if ach == True:
                                pl.exp += r.randint(10, 50)
                                pl.levelup()
                                show_text.append(dialog(f'完成挑战: {achTitle}', purple=True))
                            elif ach == False:
                                show_text.append(dialog(f'完成进度: {achTitle}', yellow=True))
                        elif pl.level == 15:
                            ach, achTitle = achieve.ask(achieve.achievement.adventure.fullLevel)
                            if ach == True:
                                pl.exp += r.randint(10, 50)
                                pl.levelup()
                                show_text.append(dialog(f'完成挑战: {achTitle}', purple=True))
                            elif ach == False:
                                show_text.append(dialog(f'完成进度: {achTitle}', yellow=True))
                        ach, achTitle = achieve.ask(achieve.achievement.adventure.haveABreakFast)
                        if ach == True:
                            pl.exp += r.randint(10, 50)
                            pl.levelup()
                            show_text.append(dialog(f'完成挑战: {achTitle}', purple=True))
                        elif ach == False:
                            show_text.append(dialog(f'完成进度: {achTitle}', yellow=True))
                    elif not (760 < pl.x < 890) or not (760 < pl.y < 920):
                        if pl.beatk < 0:
                            pl.beatk = 56
                            pl.leveldown()
                    elif config.gamemode == 'hard' and f.dtype == 'shark':
                        if pl.beatk < 0:
                            pl.beatk = 69
                            pl.leveldown()
                    else:
                        ach, achTitle = achieve.ask(achieve.achievement.adventure.impregnable)
                        if ach == True:
                            pl.exp += r.randint(10, 50)
                            pl.levelup()
                            show_text.append(dialog(f'完成挑战: {achTitle}', purple=True))
                        elif ach == False:
                            show_text.append(dialog(f'完成进度: {achTitle}', yellow=True))
            ice_spike_list_new : list = []
            for ice in ice_spike_list:
                if abs(pl.x - ice.x) < pl.hx + ice.hx and abs(pl.y - ice.y) < pl.hy + ice.hy:
                    # hit
                    if pl.beatk < 0:
                        pl.beatk = 62
                        pl.leveldown()
                if config.do_ice_spike_hit_fish == True:
                    for f in fish_list:
                        if f.force <= config.do_ice_spike_hit_fish_forcelevel and abs(f.x-ice.x) < f.hx+ice.hx and abs(f.y-ice.y) < f.hy+ice.hy:
                            f.hp = 0
                # move ice
                ice.y += ice.yspeed
                if -210 < ice.y < 1000:
                    ice_spike_list_new.append(ice)
            ice_spike_list = ice_spike_list_new
            achieve.achieve_box_mov_tps()
        else:
            pass
        if pl.level < 0:
            play_isend = True
            return 1
        
        if ftps() >= ice_spike_addtime:
            ice_spike_addtime = ftps() + 12_000
            if config.biome[biome_now]['ice_spike drops'] == True:
                for i in range(r.randint(1, 3)):
                    ice_spike_list.append(iceSpikes())
        
        local_difficulty += local_difficulty_mob_add
        if ftps() >= local_difficulty_tps:
            local_difficulty_tps = ftps() + 500
            local_difficulty_per = cint(local_difficulty*100/local_difficulty_max)
            local_difficulty_show = f'{local_difficulty:.2f}'
        
        # Rendering
        config.fps.tick(config.fpslimit if do_pausing == False else 5)
        config.screen.blit(bgpic, (0, 0))

        if ftps() >= fps_time:
            fps_time = ftps() + 1_000
            fps_get_now = config.fps.get_fps()
            hunger_reduce_calculator.push(pl.hunger_add_sum_count)
            pl.hunger_add_sum_count = 0
        
        if ftps() >= cpu_time:
            cpu_time = ftps() + 1_000
            cpu_get_now = cint(psutil.cpu_percent())
            mem_get_now = cint(psutil.virtual_memory().percent)
            cpufreq_get_now = cint(psutil.cpu_freq().current)

        rect_pl = pl.imgl.get_rect()
        rect_pl.center = (pl.x, pl.y)
        if pl.beatk > 0 and (pl.beatk % 4 == 1 or pl.beatk % 4 == 0):  # 受伤闪烁效果
            pass
        else:
            if pl.facing == 0:
                config.screen.blit(pl.imgl, rect_pl)
            else:
                config.screen.blit(pl.imgr, rect_pl)
        for f in fish_list:
            if f.dtype == 'su':
                if (abs(f.x - pl.x)<360 and abs(f.y - pl.y) < 240 ) or Phosper.get_client(f.x, f.y) >= 2 or config.gamemode == 'easy':
                    rect_pl = f.imgl.get_rect()
                    rect_pl.center = (f.x, f.y)
                    config.screen.blit(f.imgl, rect_pl)
                else:
                    pass
            else:
                rect_pl = f.imgl.get_rect()
                rect_pl.center = (f.x, f.y)
                if f.facing == 0:
                    config.screen.blit(f.imgl, rect_pl)
                else:
                    config.screen.blit(f.imgr, rect_pl)
            if do_show_box is True:
                if f.dtype == 'su':
                    if (abs(f.x - pl.x)<360 and abs(f.y - pl.y) < 240 ) or Phosper.get_client(f.x, f.y) >= 2 or config.gamemode == 'easy':
                        pygame.draw.rect(config.screen, (230, 210, 90), (cint(f.x - f.hx), cint(f.y - f.hy), cint(f.hx*2), cint(f.hy*2)), 1)
                    else:
                        pass
                else:
                    pygame.draw.rect(config.screen, (230, 210, 90), (cint(f.x - f.hx), cint(f.y - f.hy), cint(f.hx*2), cint(f.hy*2)), 1)
        if len(ice_spike_list) != 0:
            for ice in ice_spike_list:
                rect_pl = ice.pic.get_rect()
                rect_pl.center = (ice.x, ice.y)
                config.screen.blit(ice.pic, rect_pl)
        
        if config.biomefilter['render'] == True and BF != None:
            BF.render_biome()
        if 546_000 <= ftps() - sunrise_time <= 606_000:
            dusk_render.draw(sunrise_time)
        # 水草 安全区
        config.screen.blit(r.choice(config.grass_safes), (760, 760))
        # 饥饿条显示
        hunger_bar.fill((255, 0, 0))
        hunger_per : float = pl.foot / pl.footmax
        pygame.draw.rect(hunger_bar, (180, 180, 180), (2,2, 496, 20))
        pygame.draw.rect(hunger_bar, hunger_color, (2, 2, cint(496 * hunger_per), 20))
        text_bar = f2f.render(f'food:{pl.foot}', True, hunger_color_anti)
        text_bar_rect = text_bar.get_rect()
        text_bar_rect.center = (570, 902)
        config.screen.blit(hunger_bar, (320, 890))
        config.screen.blit(text_bar, text_bar_rect)
        # xp显示
        exp_max = 0
        if pl.level <= 4:
            exp_max = pl.level * 27 + 36
        elif pl.level <= 12:
            exp_max = pl.level ** 2 + 128
        else:
            exp_max = pl.level ** 3 - 1456
        xp_bar.fill((255, 0, 0))
        xp_per : float = pl.exp / exp_max
        pygame.draw.rect(xp_bar, (216, 216, 216), (2,2, 496, 20))
        pygame.draw.rect(xp_bar, xp_color, (2, 2, cint(496 * xp_per), 20))
        text_bar = f2f.render(f'level:{pl.level} xp:{pl.exp}({(xp_per*100):.2f})%', True, xp_color_anti)
        text_bar_rect = text_bar.get_rect()
        text_bar_rect.center = (1080, 902)
        config.screen.blit(xp_bar, (830, 890))
        config.screen.blit(text_bar, text_bar_rect)

        if do_pausing is True:
            ot(30, 'center', '已暂停, 按P或ESC继续', (255, 160, 20))
        else:
            if ftps() - sunrise_time < 24_100:
                # sunrise
                if light_dark_add_tps < ftps():
                    light_dark -= 1
                    if light_dark < -1:
                        light_dark = -1
                    light_dark_add_tps = ftps() + 200
            elif ftps() - sunrise_time < 582_000:
                # day
                light_dark = -1
            elif ftps() - sunrise_time < 606_100:
                # sunset
                if light_dark_add_tps < ftps():
                    light_dark += 1
                    if light_dark > 120:
                        light_dark = 120
                    light_dark_add_tps = ftps() + 200
            elif ftps() - sunrise_time < 1200_000:
                # night
                light_dark = 120
            else:
                # next day
                sunrise_time = ftps()
                day += 1
                if day % config.survival_day_mod == 0:
                    biome_now_index += 1
                    if biome_now_index >= len(biome_now_canuse):
                        # win
                        ach, achTitle = achieve.ask(achieve.achievement.survival.survivalMaster)
                        if ach == True:
                            pl.exp += r.randint(10, 50)
                            show_text.append(dialog(f'完成挑战: {achTitle}', purple=True))
                        elif ach == False:
                            show_text.append(dialog(f'完成进度: {achTitle}', yellow=True))
                        return 0
                    else:
                        biome_now = biome_now_canuse[biome_now_index]
                        is_frozen = config.biome[biome_now]['frozen']
                        BF = BiomeFilter.set_biome(biome_now, bgpic)
                        local_difficulty_max = calc_local_difficulty_max(biome_now)
                        ach, achTitle = achieve.ask_custom(achieve.achievement.custom.PopularScenicSpots, biome_now)
                        if ach == True:
                            pl.exp += r.randint(10, 50)
                            show_text.append(dialog(f'完成挑战: {achTitle}', purple=True))
                        elif ach == False:
                            show_text.append(dialog(f'完成进度: {achTitle}', yellow=True))
                ach, achTitle = achieve.ask(achieve.achievement.adventure.sweetDream)
                if ach == True:
                    pl.exp += r.randint(10, 50)
                    show_text.append(dialog(f'完成挑战: {achTitle}', purple=True))
                elif ach == False:
                    show_text.append(dialog(f'完成进度: {achTitle}', yellow=True))
            if rule_using_RT == False:
                phosphorus.draw(dark=light_dark)
            else:
                grass_Area = RenderEngine.light_block(825, 920, 14)
                Lantern_Fish_list.append(grass_Area)
                Phosper.draw(dark=light_dark, lights= tuple(Lantern_Fish_list))
                #Phosper.draw(dark=light_dark, lights=())
        if do_input_command is True:
            ot(56, 'w', cmd.text, (80, 255, 60))
        if show_text != []:
            show_text_new = []
            for i in range(len(show_text)):
                if do_show_text_past == False:
                    ot(line=55 - i, direct='w', word=show_text[i].text, colour=(100, 255, 60) if not show_text[i].red else (255, 0, 0))
                if show_text[i].times > ftps():
                    show_text_new.append(show_text[i])
                else:
                    show_text_past.append(show_text[i])
                    if len(show_text_past) > config.showTextMax:
                        show_text_past.pop(0)
            show_text = show_text_new
        
        if do_show_text_past == True:
            for i in range(len(show_text_past)):
                ot(line=55 - i, direct='w', word=show_text_past[i].text, colour=(255, 0, 0) if show_text_past[i].red == True else \
                   (255, 255, 0) if show_text_past[i].yellow == True else (255, 20, 255) if show_text_past[i].purple == True else (100, 255, 60))
        
        # show achieve
        achieve.show_achieve_box()

        if do_show_debug == True:
            ot(line=0, direct='w', word=f'FPS: {cint(fps_get_now)} Entity: {len(fish_list)}')
            ot(line=1, direct='w', word=f'Player:{pl.force if pl.level>= 0 else False} lvl:{pl.level} exp: {pl.exp} EXP: {exp_max}')
            ot(line=2, direct='w', word=f'Position: ({pl.x}, {pl.y}) Facing: {"west" if pl.facing == 0 else "east"}')
            ot(line=3, direct='w', word=f'Render time: {cint((ftps() - start_render_time)/1000)} sec')
            if rule_using_RT == False:
                ot(line=4, direct='w', word=f'Day: {day} Light: (day {255 - light_dark}, client {cint(255 - light_dark - pl.y/40)})')
            else:
                ot(line=4, direct='w', word=f'Day: {day} Light: (day {cint((255-light_dark)*15/250)}, client {Phosper.get_client(pl.x,pl.y)})')
            ot(line=5, direct='w', word=f'Foot: {pl.foot},  Reduce: {hunger_reduce_calculator.get_averge():.2f}')
            ot(line=6, direct='w', word=f'Biome: {biome_now} Local difficulty: {local_difficulty_per} ({local_difficulty_show}//{local_difficulty_max:.2f})')

            ot(line=0, direct='e', word=f'Core: Python {config.Python_vesion}, SDL {config.Pygame_SDL_version}', ex= 1650 - 49*12 + 80)
            ot(line=1, direct='e', word=f'CPU: {"" if cpu_get_now > 9 else " "}{cpu_get_now}% Mem: {mem_get_now}%' , ex=1650 - 18*12)
            ot(line=2, direct='e', word=f'CPU Frequency: {cpufreq_get_now}MHz, Driver: {pygame.display.get_driver()}', ex= 1650 - 22*12)
            ot(line=3, direct='e', word=f'Light pursuit engine: {"   Phosper" if rule_using_RT==True else "phosphorus"}', ex=1650 - 30*12 + 9)
            if config.render_engine == "strontium":
                ot(line=4, direct='e', word=f'Rendering engine: Strontium', colour=(30, 255, 30), ex=1650 - 30*12 + 9)
            else:
                ot(line=4, direct='e', word=f'Rendering engine: Starlight', colour=(30, 255, 30), ex=1650 - 30*12 + 9)
            ot(line=5, direct='e', word=f"{Optimize_Engine.get_version()}", colour=(180, 255, 116))

        ot(line=28, direct='e', word=f'加速: {"ON" if pl.Speed_i == True else "OFF"}', colour=(60, 255, 70) if pl.Speed_i == False else (255, 80, 80), ex=1500)
        
        if mem_get_now > 92:
            raise RuntimeError('Runtime Error: 内存占用超过92%。可能发生了内存泄漏。')
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play_isend = True
                return
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_F3:
                    if do_show_debug is True:
                        do_show_debug = False
                    else:
                        do_show_debug = True
                elif event.key == pygame.K_F5:
                    do_show_box = True if not do_show_box else False
                elif event.key == pygame.K_i and do_input_command == False:
                    pl.speed_half()
                elif event.key == pygame.K_t and do_input_command == False:
                    do_show_text_past = True if do_show_text_past == False else False
                elif event.key == pygame.K_ESCAPE:
                    if do_input_command == True:
                        do_input_command = False
                        cmd.clear()
                    if do_pausing == True:
                        do_pausing = False
                elif event.key == pygame.K_p:# and not do_input_command:
                    if do_pausing == False:
                        do_pausing = True
                        day_night_cycle_pause_time = ftps() - sunrise_time
                    else:
                        do_pausing = False
                elif False and event.key == pygame.K_SLASH and not do_pausing:
                    do_input_command = True
                    cmd.text = '/'
                elif event.key == pygame.K_RETURN and not do_pausing and do_input_command is True:
                    show_text.append(dialog('生存模式下命令被禁用。', red=True))
                    continue
                    if len(cmd.text) <= 1:
                        show_text.append(dialog(f'不完整的命令: {cmd.text}', red=True))
                    c = cmd.text[1:]
                    if ' ' in c:
                        c = c.split(' ')
                    else:
                        c = [c]
                    if c[0] == 'exp':
                        if len(c) != 3:
                            show_text.append(dialog(f'不完整的命令: {cmd.text[1:]}', red=True))
                        else:
                            a : str = c[1]
                            b : str = c[2]
                            if a == 'add' and b.isdigit():
                                if 0 <= int(b) <= 32767:
                                    pl.exp += int(b)
                                    pl.levelup()
                                    show_text.append(dialog(f'成功增加经验 {b}'))
                                else:
                                    show_text.append(dialog(f'错误的命令: {cmd.text[1:]} 数量应在0~32767之间', red=True))
                            elif a == 'reduce' and b.isdigit():
                                if int(b) < 0 or int(b) > 32767:
                                    show_text.append(dialog(f'错误的命令: {cmd.text[1:]} 数量应在0~32767之间',  red=True))
                                else:
                                    bb = int(b)
                                    while bb >= cint((pl.level - 1) * 27 + 36) and pl.level > 0:
                                        pl.level -= 1
                                        pl.leveldown()
                                        bb -= cint(pl.level * 27 + 36)
                                    if bb > 0:
                                        pl.exp -= bb
                                    show_text.append(dialog(f'成功减少经验 {b}'))
                            else:
                                show_text.append(dialog(f'错误的命令: {cmd.text[1:]}', red=True))
                    elif c[0] == 'speed':
                        if len(c) == 2 and c[1] == 'show':
                            show_text.append(dialog(f'Player speed: {pl.speed} -> {pl.speed if is_frozen == False else cint(pl.speed/2)}'))
                        elif len(c) == 3 and c[1] == 'set' and c[2].isdigit():
                            if 0 < int(c[2]) < 32:
                                pl.speed = int(c[2])
                                show_text.append(dialog(f'已将玩家速度设置为 {c[2]}'))
                            else:
                                show_text.append(dialog(f'错误的命令: {cmd.text[1:]} 速度应在1~31之间',  red=True))
                        else:
                            show_text.append(dialog(f'不完整的的命令: {cmd.text[1:]}',  red=True))
                    elif c[0] == 'time':
                        if len(c) == 2 and c[1] == 'query':
                            time_query_secs = cint((ftps() - sunrise_time) / 1000)
                            show_text.append(dialog(f'Day: {day}, Time: {cint(time_query_secs/60)}:{cint(time_query_secs%60)}'))
                        elif len(c) == 3 and c[1] == 'set':
                            if c[2] == 'day':
                                sunrise_time = ftps() - 6101
                                show_text.append(dialog(f'成功设置时间: {c[2]}'))
                            elif c[2] == 'sunrise':
                                sunrise_time = ftps() - 1
                                show_text.append(dialog(f'成功设置时间: {c[2]}'))
                            elif c[2] == 'sunset':
                                sunrise_time = ftps() - 600_000
                                show_text.append(dialog(f'成功设置时间: {c[2]}'))
                            elif c[2] == 'night':
                                sunrise_time = ftps() - 606_101
                                show_text.append(dialog(f'成功设置时间: {c[2]}'))
                            elif c[2] == 'dusk':
                                sunrise_time = ftps() - 539_900
                                show_text.append(dialog(f"成功设置时间: {c[2]}"))
                            else:
                                show_text.append(dialog(f'错误的时间: {c[2]}', red=True))
                        else:
                            show_text.append(dialog(f'不完整的的命令: {cmd.text[1:]}',  red=True))
                    elif c[0] == 'summon':
                        if len(c) == 4 and c[2].isdigit() and c[3].isdigit():
                            summon_name, summon_x, summon_y = c[1], int(c[2]), int(c[3])
                            if summon_name == 'small':
                                summon_entity = sf()
                                summon_entity.x = summon_x
                                summon_entity.y = summon_y
                                fish_list.append(summon_entity)
                                show_text.append(dialog(f'成功生成实体 {summon_name}'))
                            elif summon_name == 'mid':
                                summon_entity = mf()
                                summon_entity.x = summon_x
                                summon_entity.y = summon_y
                                fish_list.append(summon_entity)
                                show_text.append(dialog(f'成功生成实体 {summon_name}'))
                            elif summon_name == 'large':
                                summon_entity = lf()
                                summon_entity.x = summon_x
                                summon_entity.y = summon_y
                                fish_list.append(summon_entity)
                                show_text.append(dialog(f'成功生成实体 {summon_name}'))
                            elif summon_name == 'lantern':
                                summon_entity = Lantern_Fish()
                                summon_entity.x = summon_x
                                summon_entity.y = summon_y
                                fish_list.append(summon_entity)
                                show_text.append(dialog(f'成功生成实体 {summon_name}'))
                            elif summon_name == 'shark':
                                summon_entity = shark()
                                summon_entity.x = summon_x
                                summon_entity.y = summon_y
                                fish_list.append(summon_entity)
                                show_text.append(dialog(f'成功生成实体 {summon_name}'))
                            
                            else:
                                show_text.append(dialog(f'错误的实体id: {summon_name}', ftps()+3000, True))
                        else:
                            show_text.append(dialog(f'不完整的的命令: {cmd.text[1:]}',  red=True))
                    elif c[0] == 'list':
                        if len(c) == 2:
                            if c[1] == 'entity':
                                show_text.append(dialog('small,mid,large,lantern,shark,sea_urchin'))
                            elif c[1] == 'gamerule' or c[1] == 'rule':
                                show_text.append(dialog('renderBiome'))
                            elif c[1] == 'biome':
                                show_text.append(dialog(f'{config.biome["allow"]}'))
                            else:
                                show_text.append(dialog(f'错误的list name: {c[1]}', ftps()+3000, True))
                        else:
                            show_text.append(dialog(f'不完整的的命令: {cmd.text[1:]}',  red=True))
                    elif c[0] == 'gamerule':
                        if len(c) == 3:
                            if c[1] == 'renderBiome' and c[2] == 'true':
                                config.biomefilter['render'] = True
                                show_text.append(dialog('成功将规则"renderBiome"设置为: true'))
                            elif c[1] == 'renderBiome' and c[2] == 'false':
                                config.biomefilter['render'] = False
                                show_text.append(dialog('成功将规则"renderBiome"设置为: false'))
                            else:
                                show_text.append(dialog(f'错误的rule: {cmd.text[1:]}', ftps()+3000, True))
                        elif len(c) == 2:
                            if c[1] == 'renderBiome':
                                show_text.append(dialog(f'Rule "renderBiome" 现在是: {config.biomefilter["render"]}'))
                            else:
                                show_text.append(dialog(f'不存在此规则: {c[1]}', ftps()+3000, True))
                        else:
                            show_text.append(dialog(f'不完整的的命令: {cmd.text[1:]}',  red=True))
                    elif c[0] == 'biome':
                        if len(c) == 2:
                            if c[1] == 'show':
                                show_text.append(dialog('此命令暂未启用', red= False, yellow=True))
                        elif len(c) == 3 and c[1] == 'set':
                            #if reset_biome(c[2]) == True:
                            #    show_text.append(dialog(f'成功设置生物群系: {c[2]}'))
                            #else:
                            #    show_text.append(dialog(f'错误的生物群系id: {c[2]}', red=True))
                            biome_now_canuse_inreset:list = config.biome['allow']
                            if c[2] == 'random':
                                biome_now = r.choice(biome_now_canuse_inreset)
                                is_frozen = config.biome[biome_now]['frozen']
                                BF = BiomeFilter.set_biome(biome_now, bgpic)
                                local_difficulty_max = calc_local_difficulty_max(biome_now)
                                ach, achTitle = achieve.ask_custom(achieve.achievement.custom.PopularScenicSpots, biome_now)
                                if ach == True:
                                    pl.exp += r.randint(10, 50)
                                    show_text.append(dialog(f'完成挑战: {achTitle}', purple=True))
                                elif ach == False:
                                    show_text.append(dialog(f'完成进度: {achTitle}', yellow=True))
                            else:
                                if c[2] in biome_now_canuse_inreset:
                                    biome_now = c[2]
                                    is_frozen = config.biome[biome_now]['frozen']
                                    BF = BiomeFilter.set_biome(biome_now, bgpic)
                                    local_difficulty_max = calc_local_difficulty_max(biome_now)
                                    ach, achTitle = achieve.ask_custom(achieve.achievement.custom.PopularScenicSpots, biome_now)
                                    if ach == True:
                                        pl.exp += r.randint(10, 50)
                                        show_text.append(dialog(f'完成挑战: {achTitle}', purple=True))
                                    elif ach == False:
                                        show_text.append(dialog(f'完成进度: {achTitle}', yellow=True))
                                else:
                                    show_text.append(dialog(f'错误的生物群系id: {c[2]}', red=True))
                        else:
                            show_text.append(dialog(f'不完整的的命令: {cmd.text[1:]}',  red=True))
                    elif c[0] == 'gamemode':
                        if len(c) == 1:
                            show_text.append(dialog(f'当前模式: {config.gamemode}'))
                        elif len(c) == 2:
                            if c[1] in ('easy', 'hard'):
                                config.gamemode = c[1]
                                show_text.append(dialog(f'成功设置游戏模式: {c[1]}'))
                            else:
                                show_text.append(dialog(f'不存在此模式: {c[1]}', red=True))
                        else:
                            show_text.append(dialog(f'不完整的的命令: {cmd.text[1:]}',  red=True))
                    elif c[0] == 'help':
                        if len(c) == 1:
                            show_text.append(dialog('exp, speed, time, summon, list, gamerule, biome, gamemode'))
                        elif len(c) == 2:
                            if c[1] == 'exp':
                                show_text.append(dialog('exp [add/reduce] <num:int>'))
                            elif c[1] == 'speed':
                                show_text.append(dialog('speed [show/set] (<num:int>)'))
                            elif c[1] == 'time':
                                show_text.append(dialog('time [query/set] (null/[day/sunrise/dusk/sunset/night])'))
                            elif c[1] == 'summon':
                                show_text.append(dialog('summon <entity:str> <x:int> <y:int>'))
                            elif c[1] == 'list':
                                show_text.append(dialog('list [entity | gamerule | biome]'))
                            elif c[1] == 'gamerule':
                                show_text.append(dialog('gamerule [renderBiome] (<value:bool>)'))
                            elif c[1] == 'biome':
                                show_text.append(dialog('biome [show | set] (biomename:str)'))
                            else:
                                show_text.append(dialog(f'错误的命令: {cmd.text[1:]}',  red=True))
                        else:
                            show_text.append(dialog(f'错误的命令: {cmd.text[1:]}',  red=True))
                    else:
                        show_text.append(dialog(f'无效的命令: {c[0]}', red=True))
                    do_input_command = False
                    cmd.clear()
                elif do_input_command == True:
                    # input command
                    k = event.key
                    cmd << k
                    
        pygame.display.flip()
    BiomeFilter.uninstall()
        
f = pygame.font.Font('C:/Windows/Fonts/simhei.ttf', 0x12)
def ot(line : int, direct : str = 'w', word : str = 'OOOO', colour = config.outtextcolor, ex : int = 1450):
    """out text"""
    y = line * 15 + 10
    x = 20
    if direct == 'w':
        pass
    elif direct == 'e':
        x = 1630
    else:
        x = 810
    text = f.render(word, True, colour)
    if direct == 'w' or direct == 'center':
        config.screen.blit(text, (x, y))
    elif direct == 'e':
        rect = text.get_rect()
        rect.topright = (x, y)
        config.screen.blit(text, rect)
    else:
        config.screen.blit(text, (x, y))

def graphic():
    global play_isend
    fps_time = ftps()
    fps_get_now = 0
    start_render_time = ftps()
    while not play_isend and not config.graphic_isend and not config.main_isend:
        config.fps.tick(1000)
        config.screen.blit(bgpic, (0, 0))

        if ftps() >= fps_time:
            fps_time = ftps() + 1_000
            fps_get_now = config.fps.get_fps()
        ot(line=0, direct='w', word=f'FPS: {fps_get_now} Entity: {len(fish_list)}')
        ot(line=1, direct='w', word=f'Player:{1 if pl.level>= 0 else False} lvl:{pl.level} exp:{pl.exp} Force:{pl.force}')
        ot(line=2, direct='w', word=f'Position: ({pl.x}, {pl.y}) Facing: {"west" if pl.facing == 0 else "east"}')
        ot(line=3, direct='w', word=f'Render time: {cint((ftps() - start_render_time)/1000)} sec')

        rect_pl = pl.imgl.get_rect()
        rect_pl.center = (pl.x, pl.y)
        if pl.facing == 0:
            config.screen.blit(pl.imgl, rect_pl)
        else:
            config.screen.blit(pl.imgr, rect_pl)
        for f in fish_list.copy():
            rect_pl = f.imgl.get_rect()
            rect_pl.center = (f.x, f.y)
            if f.facing == 0:
                config.screen.blit(f.imgl, rect_pl)
            else:
                config.screen.blit(f.imgr, rect_pl)
            
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play_isend = True
        pygame.display.flip()

def start():
    global bgpic, play_isend, gpu_isend, fish_list, pl
    bgpic = r.choice(config.bgs)
    play_isend = False
    gpu_isend = False
    fish_list = []
    pl = player()
    #threading.Thread(target=graphic, daemon=True).start()
    play()
    return